package online.heycm;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;

import java.io.*;

/**
 * PDF Document
 *
 * @author hey
 * @version 1.0
 * @since 2024-8-16 20:42:19
 */
public abstract class PDF implements Closeable {

    private PDDocument document;
    private PDPage currentPage;
    private PDFont font;
    private float fontSize;

    /**
     * 加载PDF文件
     */
    public void load() {
        this.operate(() -> {
            InputStream is = this.initInputStream();
            assert null != is;
            document = PDDocument.load(is);
            is.close();
        });
    }

    /**
     * 切换页面
     *
     * @param pageIndex 页面索引
     */
    public void switchPage(int pageIndex) {
        currentPage = document.getPage(pageIndex);
    }

    /**
     * 添加页面
     *
     * @param mediaBox 页面尺寸
     */
    public void addPage(PDRectangle mediaBox) {
        this.operate(() -> document.addPage(new PDPage(mediaBox)));
    }

    /**
     * 设置字体
     *
     * @param font     字体
     * @param fontSize 字体大小
     */
    public void setFont(PDFont font, float fontSize) {
        this.font = font;
        this.fontSize = fontSize;
    }

    /**
     * 设置字体
     *
     * @param fontFile 字体文件
     * @param fontSize 字体大小
     */
    public void setFont(File fontFile, float fontSize) {
        this.operate(() -> {
            PDFont font = PDType0Font.load(document, new FileInputStream(fontFile), false);
            setFont(font, fontSize);
        });
    }

    /**
     * 设置字体
     *
     * @param fontIS   字体输入流
     * @param fontSize 字体大小
     */
    public void setFont(InputStream fontIS, float fontSize) {
        this.operate(() -> {
            PDFont font = PDType0Font.load(document, fontIS, false);
            setFont(font, fontSize);
        });
    }

    /**
     * 添加文本
     *
     * @param tx   x坐标
     * @param ty   y坐标
     * @param text 文本
     */
    public void appendText(float tx, float ty, String text) {
        this.operate(() -> {
            // 创建内容流，用于向页面添加内容
            try (PDPageContentStream contentStream = new PDPageContentStream(document, currentPage, PDPageContentStream.AppendMode.APPEND, true, true)) {
                // 开始文本块
                contentStream.beginText();
                // 设置字体和大小
                contentStream.setFont(font, fontSize);
                // 设置文本位置（以页面左下角为原点，x和y坐标）
                contentStream.newLineAtOffset(tx, ty);
                // 写入文本
                contentStream.showText(text);
                // 结束文本块
                contentStream.endText();
            }
        });
    }

    /**
     * 保存PDF文件
     */
    public void save() {
        this.operate(() -> {
            OutputStream os = initOutputStream();
            assert null != os;
            document.save(os);
        });
    }

    /**
     * 关闭PDF文件
     */
    @Override
    public void close() {
        if (null != document) {
            try {
                document.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 初始化PDF输入流
     *
     * @return 输入流
     */
    protected abstract InputStream initInputStream() throws IOException;

    /**
     * 初始化PDF输出流
     *
     * @return 输出流
     */
    protected abstract OutputStream initOutputStream() throws IOException;

    private void operate(Operate operate) {
        try {
            operate.apply();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
